
/***************************** 68000 SIMULATOR ****************************

File Name: CODE3.C
Version: 1.0

The instructions implemented in this file are the integer arithmetic
	operations:

		ADD, ADDA, ADDI, ADDQ, ADDX, SUB, SUBA, SUBI, SUBQ, SUBX

   Modified: Chuck Kelly
             Monroe County Community College
             http://www.monroeccc.edu/ckelly

***************************************************************************/


#include <stdio.h>
#include "extern.h"         /* contains global "extern" declarations */




int	ADD()
{
  int	addr_modes_mask; 	/* holds mask for use in 'eff_addr()' */
  int	reg;		 	/* holds data register number */
  long	size;		 	/* holds instruction size */
  int     error;

  /* compute addressing modes mask from appropriate bit in instruction WORD_MASK */
  addr_modes_mask = (inst & 0x0100) ? MEM_ALT_ADDR : ALL_ADDR;

  /* the following statement decodes the instruction size using */
  /* decode_size(), then decodes the effective address using eff_addr() */
  if (decode_size(&size)) 	// if size is bad then return 'bad instruction'
    return (BAD_INST);

  error = eff_addr(size, addr_modes_mask, true);
  if (error)              // if address error
    return error;         // return error code

  reg = (inst >> 9) & 0x0007;		/* get data register number */

  /* now perform the addition according to instruction format */
  /* instruction bit 8 'on' means data register to effective-address */
  /* instruction bit 8 'off' means effective-address to data register */
  if (inst & 0x0100) {
          source = D[reg];
          dest = EV1;
          put (EA1, source + dest, size);	/* use 'put' to deposit result */
          value_of (EA1, &result, size);	/* set 'result' for use in 'cc_update' */
          }
  else {
          source = EV1;		/* get source. 'EV1' was set in 'eff_addr()' */
          dest = D[reg];		/* get destination */
          put (&D[reg], source + dest, size);
          result = D[reg];	/* set 'result' for use in 'cc_update()' */
          }

  /* update the condition codes */
  cc_update (GEN, GEN, GEN, CASE_1, CASE_5, source, dest, result, size, 0);

  /* now update the cycle counter appropriately depending on the instruction */
  /* size and the instruction format */
  if (inst & 0x0100) {          // if (<ea>)+(<Dn>) --> <ea>
    inc_cyc ( (size == LONG_MASK) ? 12 : 8);
  } else {
    if (size == LONG_MASK) {
      if ( (!(inst & 0x0030)) || ((inst & 0x003f) == 0x003c) )
        inc_cyc (8);
      else
        inc_cyc (6);
    } else {
      inc_cyc (4);
    }
  }

  /* return 'success' */
  return SUCCESS;

}


int	ADDA()
{
  long	size;
  int	reg;

  if (inst & 0x0100)
          size = LONG_MASK;
  else
          size = WORD_MASK;

  int error = eff_addr(size, ALL_ADDR, true);
  if (error)              // if address error
    return error;         // return error code


  reg = (inst >> 9) & 0x0007;

  if (size == WORD_MASK)                          // ck 1/22/2003
          sign_extend ((int)EV1, WORD_MASK, &EV1);

  source = EV1;
  dest = A[a_reg(reg)];

  //put (&A[a_reg(reg)], source + dest, size);    CK 11/6/2002
  put (&A[a_reg(reg)], source + dest, LONG_MASK); // always uses 32bits of Areg


  if (size == LONG_MASK) {
          if ( (!(inst & 0x0030)) || ((inst & 0x003f) == 0x003c) )
                  inc_cyc (8);
          else
                  inc_cyc (6);
          }
  else
          inc_cyc (8);

  return SUCCESS;

}


//--------------------------------------------------------------------------
int	ADDI()
{
  long	size;

  if (decode_size(&size))
    return (BAD_INST);			// bad instruction format

  mem_request (&PC, size, &source);

  int error = eff_addr(size, DATA_ALT_ADDR, true);
  if (error)              // if address error
    return error;         // return error code

  dest = EV1;
  put (EA1, source + dest, size);
  value_of (EA1, &result, size);
  cc_update (GEN, GEN, GEN, CASE_1, CASE_5, source, dest, result, size, 0);

  if (inst & 0x0038) {
    inc_cyc ( (size == LONG_MASK) ? 20 : 12);
  } else {
    inc_cyc ( (size == LONG_MASK) ? 16 : 8);
  }
  return SUCCESS;
}



//----------------------------------------------------------------------------
int	ADDQ()
{
  long	size;

  if (decode_size(&size))
          return (BAD_INST);			/* bad size format */

  if ((inst & 0x38) == 0x8)		/* if address reg direct, operation is long */
          size = LONG_MASK;

  int error = eff_addr(size, ALTERABLE_ADDR, true);
  if (error)              // if address error
    return error;         // return error code

  source = ((inst >> 9) & 0x07);
  if (source == 0) 			/* if source is '0', that corresponds to '8' */
          source = 8;

  dest = EV1;

  put (EA1, source + dest, size);
  value_of (EA1, &result, size);

  if (!((inst & 0x38) == 0x8))   /* if address reg direct, cc's not affected */
  cc_update (GEN, GEN, GEN, CASE_1, CASE_5, source, dest, result, size, 0);

  switch (inst & 0x0038) {
          case 0x0  : inc_cyc ( (size == LONG_MASK) ? 8 : 4);
                      break;
          case 0x8  : inc_cyc (8);        // CK 12-17-2005
                      break;
          default   : inc_cyc ( (size == LONG_MASK) ? 12 : 8);
                      break;
          }

  return SUCCESS;

}



//----------------------------------------------------------------------------
int	ADDX()
{
  long	size;
  int	Rx, Ry;

  if (decode_size(&size)) return (BAD_INST);

  Rx = (inst >> 9) & 0x0007;
  Ry = inst & 0x0007;

  /* perform the ADDX operation */
  if (inst & 0x0008)
          {
          Rx = a_reg (Rx);
          Ry = a_reg (Ry);
          if (size == BYTE_MASK) {
                  A[Rx]--;
             A[Ry]--;
             }
          if (size == WORD_MASK) {
                  A[Rx] -= 2;
                  A[Ry] -= 2;
             }
          if (size == LONG_MASK) {
                  A[Rx] -= 4;
                  A[Ry] -= 4;
             }
          mem_req ((int) A[Ry], size, &source);
          mem_req ((int) A[Rx], size, &dest);
          put ((long *)&memory[A[a_reg(Rx)]], source + dest + ((SR & xbit) >> 4), size);
          mem_req ((int) A[Rx], size, &result);
          }
  else
          {
          source = D[Ry] & size;
          dest = D[Rx] & size;
          put (&D[Rx], source + dest + ((SR & xbit) >> 4), size);
          result = D[Rx] & size;
          }

  cc_update (GEN, GEN, CASE_1, CASE_1, CASE_5, source, dest, result, size, 0);

  if (size == LONG_MASK)
     inc_cyc ( (inst & 0x0008) ? 30 : 8);
  else
     inc_cyc ( (inst & 0x0008) ? 18 : 4);

  return SUCCESS;

}



//----------------------------------------------------------------------------
int	SUB()
{
  int	addr_modes_mask, reg;
  long	size;

  addr_modes_mask = (inst & 0x0100) ? MEM_ALT_ADDR : ALL_ADDR;

  if (decode_size(&size))
    return (BAD_INST);	// bad instruction format

  int error = eff_addr(size, addr_modes_mask, true);
  if (error)              // if address error
    return error;         // return error code

  reg = (inst >> 9) & 0x0007;

  if (inst & 0x0100)
          {
          source = D[reg] & size;
          dest = EV1 & size;
          put (EA1, dest - source, size);
          value_of (EA1, &result, size);
          }
  else
          {
          source = EV1 & size;
          dest = D[reg] & size;
          put (&D[reg], dest - source, size);
          result = D[reg] & size;
          }

  cc_update (GEN, GEN, GEN, CASE_2, CASE_6, source, dest, result, size, 0);

  if (inst & 0x0100) {
          inc_cyc ( (size == LONG_MASK) ? 12 : 8);
          }
  else {
          if (size == LONG_MASK) {
                  if ( (!(inst & 0x0030)) || ((inst & 0x003f) == 0x003c) )
                          inc_cyc (8);
                  else
                          inc_cyc (6);
                  }
          else {
                  inc_cyc (4);
                  }
          }

  return SUCCESS;

}


//----------------------------------------------------------------------------
int	SUBA()
{
  long	size;
  int	reg;

  if (inst & 0x0100)
          size = LONG_MASK;
  else
          size = WORD_MASK;

  int error = eff_addr(size, ALL_ADDR, true);
  if (error)              // if address error
    return error;         // return error code

  reg = (inst >> 9) & 0x0007;

  if (size == WORD_MASK)                          // ck 1/22/2003
          sign_extend ((int)EV1, WORD_MASK, &EV1);

  source = EV1;
  dest = A[a_reg(reg)];

  //put (&A[a_reg(reg)], dest - source, size);    // CK 11/6/2002
  put (&A[a_reg(reg)], dest - source, LONG_MASK); // always uses 32bits of Areg

  if (size == LONG_MASK) {
          if ( (!(inst & 0x0030)) || ((inst & 0x003f) == 0x003c) )
                  inc_cyc (8);
          else
                  inc_cyc (6);
          }
  else
          inc_cyc (8);

  return SUCCESS;

}


//----------------------------------------------------------------------------
int	SUBI()
{
  long	size;

  if (decode_size(&size))
    return (BAD_INST);		     // bad instruction format

  mem_request (&PC, size, &source);

  int error = eff_addr(size, DATA_ALT_ADDR, true);
  if (error)              // if address error
    return error;         // return error code

  dest = EV1;

  put (EA1, dest - source, size);
  value_of (EA1, &result, size);

  cc_update (GEN, GEN, GEN, CASE_2, CASE_6, source, dest, result, size, 0);

  if (inst & 0x0038) {
          inc_cyc ( (size == LONG_MASK) ? 20 : 12);
          }
  else {
          inc_cyc ( (size == LONG_MASK) ? 16 : 8);
          }

  return SUCCESS;

}



//----------------------------------------------------------------------------
int	SUBQ()
{
  long	size;

  if (decode_size(&size))
    return (BAD_INST);			/* bad size format */

  if ((inst & 0x38) == 0x8)		/* if address reg direct, operation is long */
          size = LONG_MASK;

  int error = eff_addr(size, ALTERABLE_ADDR, true);
  if (error)              // if address error
    return error;         // return error code

  source = ((inst >> 9) & 0x07);
  if (source == 0) 			/* if source is '0', that corresponds to '8' */
          source = 8;

  dest = EV1;

  put (EA1, dest - source, size);
  value_of (EA1, &result, size);

  if (!((inst & 0x38) == 0x8))   /* if address reg direct, cc's not affected */
  cc_update (GEN, GEN, GEN, CASE_2, CASE_6, source, dest, result, size, 0);

  switch (inst & 0x0038) {
          case 0x0  : inc_cyc ( (size == LONG_MASK) ? 8 : 4);
                      break;
          case 0x8  : inc_cyc (8);        // CK 12-17-2005
                      break;
          default   : inc_cyc ( (size == LONG_MASK) ? 12 : 8);
                      break;
          }

  return SUCCESS;

}




//----------------------------------------------------------------------------
int	SUBX()
{
  long	size;
  int	Rx, Ry;

  if (decode_size(&size))
    return (BAD_INST);

  Ry = (inst >> 9) & 0x0007;
  Rx = inst & 0x0007;

  /* perform the SUBX operation */
  if (inst & 0x0008)
          {
          Rx = a_reg(Rx);
          Ry = a_reg(Ry);
          if (size == LONG_MASK)
                  {
                  A[Rx] -= 4;
                  A[Ry] -= 4;
                  }
          else if (size == WORD_MASK)
                  {
                  A[Rx] -= 2;
                  A[Ry] -= 2;
                  }
          else
                  {
                  A[Rx]--;
                  A[Ry]--;
                  }
          mem_req ( (int) A[Rx], size, &source);
          mem_req ( (int) A[Ry], size, &dest);
          put ((long *)&memory[A[Ry]], dest - source - ((SR & xbit)>> 4), size);
          mem_req ( (int) A[Ry], size, &result);
          }
  else
          {
          source = D[Rx] & size;
          dest = D[Ry] & size;
          put (&D[Ry], dest - source - ((SR & xbit) >> 4), size);
          result = D[Ry] & size;
          }

  cc_update (GEN, GEN, CASE_1, CASE_2, CASE_6, source, dest, result, size, 0);

  if (size == LONG_MASK)
     inc_cyc ( (inst & 0x0008) ? 30 : 8);
  else
     inc_cyc ( (inst & 0x0008) ? 18 : 4);

  return SUCCESS;

}

